iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
3
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 13 篇

Day12 | React 的快樂小夥伴 - Redux 資料管理篇

  • 分享至 

  • xImage
  •  

前言

Redux 是一個全域的資料管理工具,更簡單來說,

就是能替你管理整個網站需要的 State,

所有的資料都能放在 Redux,需要資料的 Component,也直接從 Redux 取得,所以

組件管理 state 變得更方便,而且也確保整個專案的資料都來自同一個地方。

除此之外,如果我們在 Component 內寫下一堆關於獲取資料的 API 請求,那程式就會變得非常的亂,且發生問題時也不容易找到,而 Redux 在專案內的角色還能夠讓「畫面 Component」及「資料 Redux」分離,

有關畫面的就修改 React 的 component,資料的部分就完全交給 Redux,每個人各司其職、分工合作。

最後更令人開心的是,在 Redux 新的版本 v7.1.0 中,也申裝上更符合 Hooks 的使用方法了!所以用起來會比去年更親民而且好上手!

說了那麼多,伸出你的手,擁抱 Redux 吧 :)


前置準備

  1. 文中的專案會以 Day11 的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 一顆擁有學習熱忱的心。

使用方法

安裝 Redux

redux 是函式庫本身,react-redux 則是 Redux 與 React 的綁定使用套件。

npm install --save redux react-redux

安裝完後,接下來會一步一步說明 Redux 內的核心組成。

Reducer

Reducer 在 Redux 中是用來保管 state,以及在接收到不同的 action 指令時該對 state 做什麼動作的函數。

先打開 clone 下來的專案,並在 ./src 目錄下建立一個 reducer 的資料夾,並在裡面新增一個檔案叫做 todolist.js,像這樣:

|- src
 |-reducer
  |-todolist.js

接著打開 .src/reducer/todolist.js,首先要為該 Reducer 設計它所管理的 State 架構,下方沿用上一篇的 todolist 的 State 格式:

const initState = {
  todoList: ['第一件事情', '第二件事情'],
};

在 initState 中另外將資料架構清楚列出,能讓接手的人或是兩個禮拜後的自己,一看就能知道這個 Reducer 保管了哪些資料,且預設了初始資料的格式,也比較不會讓 Component 得到錯誤的 State 格式而 Render 失敗。

現在有了初始資料,就可以使用它建立一個 Reducer:

const todoReducer = (state = initState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default todoReducer;

每一個 Reducer 都會有兩個參數,第一個參數會將初始的資料狀態 initState 交由 Reducer 保管,第二個參數會傳入現在 Reducer 要對 State 做什麼動作的指令及額外的參數,這些在明天會接著講解。

所以在還沒有任何 action 指令描述的 Reducer 內,預設回傳了它所保管的 State ,在這裡就是上方的 initState。

記得要將 Reducer 給 export,否則在下一步創建 Store 的時候會沒辦法 import。

Store

Store 的工作就是在應用程式中負責整合所有的 Reducer,這裡我會在 src 中為 Store 創建屬於它的目錄,如下:

|- src
 |-store
  |-index.js

接著打開 src/store/index.js,因為我們只有一個 Reducer,所以直接 import todoReducer,並將它提供給 createStore 創建一個 Store 就行了,最後要記得將 Store export 出去:

import { createStore } from 'redux';
import todoReducer from '../reducer/todolist';

const store = createStore(todoReducer);

export default store;

如果有多個 Reducer 的情況,就得使用 combileReducer 先將 Reducer 給組合起來,再將組合後的結果送給 createStore 創建 Store:

import { createStore, combineReducers } from 'redux';
import todoReducer from '../reducer/todolist';
import otherReducer from '../reducer/other';

const rootReducer = combineReducers({
  todoReducer,
  otherReducer,
});

const store = createStore(rootReducer);

Provider

Provider 是 react-redux 中的組件,它會接收上方創建的 Store,記得上一篇的 createContext 創建出來的 Component 嗎?其實 Provider 在做的事情就和它一樣,在 Component 的最上層用 Store 管理所有 Reducer ,而 Reducer 則是管理 State。

Provider 設置的位置會在整個 Project 的最上層,也就是各位最後使用 ReactDOM.render 指定的那個 Component,以當前手上的專案來說 ReactDOM.render 位於 src/index.js 裡面,而被它指定 Render 的就是 Main,因此在這裡 Main 就是專案裡最上層的 Component,而 Provider 就得包著它,使用的同時也必須提供 store 的 Props 給 Provider :

useSelector

useSelector 是新版 react-redux 所增加的 Hooks,功能就像我們使用了 useContext,只是換成 useSelector 而已:

相同道理 CurrentTask 也可以用相同的方式獲取 todoList 中的資料:

移除 State 及 createContext

由於我們已經將 todolist 中的資料交給 Redux 保管, Component 內不需再使用 State 紀錄著相同的資料,留著也不會使用它,所以直接將 Main 中的 todolist 移除。

同樣的道理,因為 Main 裡面已經沒有 State 了,而且 TodoList 和 CurrentTask 只需透過 useSelect 就能獲取 todolist 的資料,也不需特別從 Main 中以 useContext 送給其他的 Component。

移除了 useState 和 useContext 後,程式碼大概會像這樣子:

看!Component 的內容變得單純了,因為我們將資料層抽離在 Redux 中, Component 就只需要向 Redux 取資料,然後負責畫面的呈現。

本文的範例程式碼會提供在 GitHub 上,歡迎各位參考:)


結尾

關於最後提到的那段話:

「將資料隔離在 Redux,讓 Component 只處理畫面。」

真的很令人興奮,它使你能在處理畫面時不需要管任何關於資料的邏輯,處理資料邏輯時也不需要管畫面,更重要的是,

將畫面和資料區分出來,便不會使得 Component 內隨著專案越來越大而變得一團糟!

通常體會到這個真理時,都是表明著你在維護時對 Component 感到苦惱,你會在 Component 裡面的 Method 中跳躍著尋找 Bug 在哪,我不能說這是各位的必經之路,但至少就我而言,初期還滿常遇到這種狀況的,主要還是各位在遇到問題時,能試著去思考該怎麼解決,

並且想到就寫!即使它是錯的,但只要你不停止思考,便能在一次次的錯誤中找到最棒的設計模式!

如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!


上一篇
Day11 | 你有傳送門,我有 useContext!
下一篇
Day13 | React 的快樂小夥伴 - Redux 事件處理篇
系列文
在 React 生態圈內打滾的一年 feat. TypeScript 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
matuyou0301
iT邦新手 5 級 ‧ 2021-04-15 12:48:10

Redux可以將資料隔離,聽起來很酷炫! 有種MVC的既視感 xDD。

不過還是想在這裡請問一下神Q有關於createContext和useSelector的差別,
使用createContext的情況下是因為仍舊把state放在組件當中,但是如果同時用這兩種方法,一個是Redux作管理,另一個是某元件裡面也有state管理的情況下,那同時使用這兩種方法會不會很奇怪。

我的想法是雖然可以統一管理資料,但是組件內或許存在一些較特殊或者小的資料是不需要讓Redux去管理的資料,再麻煩神Q大大解惑,QQ。

我要留言

立即登入留言